/**
 * 
 * 自定义Promise模块
 */
(function(window) {
  const PENDING = 'pending' 
  const RESOLVED = 'resolved'
  const REJECTED = 'rejected'

  // Promise构造函数
  function Promise(excutor) {
    const self = this 
    self.status = PENDING // 给promise对象指定一个存放状态的属性 初始状态为pending
    self.data = undefined // 给promise对象指定一个用于存储结果数据的属性
    self.callbacks = [] // 每个元素的结构({onResolved(){}, onRejected(){}})

    // 定义resolve函数
    function resolve(value) {
      // 判断如果值不为pending,直接结束
      if(self.status !== PENDING) return

      self.status = RESOLVED //调用resolve时,把状态改为resolved
      self.data = value // 把value数据保存在data中

      // 如果callbacks里有值,立刻异步执行onResolved
      if(self.callbacks.length > 0) {
        // 放入队列中执行所有成功回调
        setTimeout(() => {
          self.callbacks.forEach(cbsObj => {
            cbsObj.onResolved(value)
          })
        })
      }
    }

    // 定义reject函数
    function reject(reason) {
       // 判断如果值不为pending,直接结束
       if(self.status !== PENDING) return

      self.status = REJECTED // 调用reject时,把状态改为rejected
      self.data = reason // 把reason数据保存在data中

      // 如果callbacks里有值,立刻异步执行onRejected
      if(self.callbacks.length > 0) {
        // 放入队列中执行所有失败回调
        setTimeout(() => {
          self.callbacks.forEach(cbsObj => {
            cbsObj.onRejected(reason)
          })
        })
      }
    }

    // 立即同步执行excutor,两个参数均为函数
    try {
      excutor(resolve, reject)
    } catch (error) { // 如果执行器抛出异常, promise对象变为rejected状态
      reject(error)
    }
  }

  /*
  用来指定成功/失败回调函数的方法
    1. 如果当前promise为resolved,异步执行成功的回调函数onResolved
    2. 如果当前promise为rejected,异步执行失败的回调函数onRejected
    3. 如果当前promise为pending,保存回调函数
  返回一个新的promise对象
    它的结果状态由onResolved和onRejected执行的结果决定
    1. 抛出error ==> 变为rejected, 结果值为error
    2. 返回值不是promise ==> 变为resolved, 结果值为返回值
    3. 返回值是promise ==> 由这个promise的值决定新的promise的结果(成功/失败)
  */
  Promise.prototype.then = function(onResolved, onRejected) {
    const self = this // 保存promise实例对象

    // 将value向下传递
    onResolved = typeof onResolved === 'function' ? onResolved : value => value
    // 将reason向下传递,错误穿透
    onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}

    // 返回新的promise对象
    return new Promise((resolve, reject) => {

      // 封装一个保存公共代码的函数
      function handle(callback) {
        try {
          const result = callback(self.data)
          // 2.2 结果值如果为promise,由这个promise的值决定新的promise的结果(成功/失败)
          if(result instanceof Promise) {
            result.then(
              value => resolve(value),
              reason => reject(reason)
            )
          // 2.3 结果值不为promise,状态改变为resolved,返回这个结果值
          } else {
            resolve(result)
          }
        } catch (error) {
          // 2.1 如果抛出error,状态变为rejected,结果值为error
          reject(error)
        }
      }

      // 1. 如果当前promise为resolved,异步执行成功的回调函数onResolved
      if(self.status === RESOLVED) {
        setTimeout(() => {
          handle(onResolved)
        })

      // 2. 如果当前promise为rejected,异步执行失败的回调函数onRejected 
      } else if(self.status === REJECTED) {
        setTimeout(() => {
          handle(onRejected)
        })

      // 3. 如果当前promise为pending,保存回调函数
      } else {
        self.callbacks.push({
          onResolved() {
            handle(onResolved)
          },
          onRejected() {
            handle(onRejected)
          }
        })
      }
    })
  }

  // 用来指定失败回调函数的方法,返回一个新的promise对象
  Promise.prototype.catch = function(onRejected) {
    // this为Promise实例,实例上可以使用原型上的方法
    return this.then(undefined, onRejected)
  }

  /*
  用来返回一个指定value的成功的promise
  value可能是一个一般值,也可能是Promise对象
  */
  Promise.resolve = function(value) {
    return new Promise((resolve, reject) => {
      // value是promise
      if(value instanceof Promise) { // 使用value的结果作为promise的结果
        // value.then(
        //   val => {resolve(val)},
        //   reason => {reject(reason)}
        // )
        // 简写
        value.then(resolve, reject)
      // value不是promise,把状态改为resolved,数据是value
      } else {
        resolve(value)
      }
    })
  }

  // 用来返回一个指定reason的失败的promise
  Promise.reject = function(reason) {
    // 返回一个失败的promise
    return new Promise((resolve, reject) => {
      reject(reason)
    })
  }

  // 返回一个新的promise对象,只有promises中所有的promise都产生成功value时才成功,否则失败
  Promise.all = function(promises) {
    const values = new Array(promises.length) // 用于保存所有成功value的数组
    let valuesCount = 0 // 记录promises中成功的个数
    return new Promise((resolve, reject) => {
      promises.forEach((p, index) => {
        p.then(
          value => {
            valuesCount++
            // p成功,将value存到values数组中
            values[index] = value
            // 如果valuesCount的值等于promises的个数,则表示都成功
            if(valuesCount === promises.length) {
              resolve(values)
            }
          }, 
          reason => reject(reason) //一旦有失败,将return变为失败
        )
      })
    })
  }

  // 返回一个新的promise对象,由第一个完成的promise决定
  Promise.race = function(promises) {
    return new Promise((resolve, reject) => {
      // 遍历所有promise,取对应结果
      promises.forEach(p => {
        p.then(
          value => resolve(value), // 一旦有成功,将return变为成功
          reason => reject(reason) // 如果有失败,将return变为失败
        )
      })
    })
  }

  // 返回一个延迟指定时间才成功的promise
  Promise.resolveDelay = function(value, time) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        // 如果value是promise
        if(value instanceof Promise) {
          value.then(
            value => resolve(value),
            reason => reject(reason)
          )
        // 如果value不是promise
        } else {
          resolve(value)
        }
      }, time)
    })
  }

  // 返回一个延迟指定时间才失败的promise
  Promise.rejectDelay = function(reason, time) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        reject(reason)
      }, time)
    })
  }

  // 向外暴露Promise
  window.Promise = Promise

})(window)